using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.Profile;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Text;
using Commerce.Common;
using Commerce.Providers;
using Commerce.PayPal;

namespace CommerceForUmbraco.usercontrols
{
    public partial class MultiPageCheckout3 : System.Web.UI.UserControl
    {
        // the current order - for shipping and tax calcs
        protected Order currentOrder = null;
        // the Umbraco Member Profile
        protected CommerceForUmbraco.profile.UmbracoProfile umbProfile;

        /// <summary>
        /// Handles the Load event of the Page control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        protected void Page_Load(object sender, EventArgs e)
        {
            try
            {
                if (!IsPostBack)
                {
                    // check the URL for a token from PayPal using Express Checkout
                    if (Utility.GetParameter("token") != string.Empty)
                    {
                        //get the info from PayPal about this buyer
                        ProcessExpressReturn(Utility.GetParameter("token"));

                        // display the 'complete checkout' link
                        completeCheckout.Visible = true;
                        ppCheckoutlabel.Visible = true;
                        paymentBox.Visible = false;
                    }

                    // set the shipping options first time we're here
                    SetCurrentOrder();
                    SetProfile(currentOrder.UserName);
                    SetOrderInfo();
                    SetShippingOptions();
                }
                else
                {
                    // get the order and set the order display
                    SetCurrentOrder();
                    SetProfile(currentOrder.UserName);
                    SetOrderInfo();
                }

                // update running total display
                lblOrderItems.Text = currentOrder.mcOrderItemsTable();
                orderRunningTotal.Text = currentOrder.OrderTotal.ToString("c");
            }
            catch (Exception ex)
            {
                message.Text = "We're sorry, an error occured.<br />" + ex.Message;
            }
        }

        #region button handlers

        /// <summary>
        /// Handles the Click event of the completeCheckout control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        protected void completeCheckout_Click(object sender, EventArgs e)
        {
            string transactionID = "";

            try
            {
                // save the address data to the profile - for convenience
                SaveAddresses();
                
                SetCurrentOrder();
                SetOrderInfo();

                MembershipUser mUser = Membership.GetUser(currentOrder.Email);

                // update the order with the current user name if not set, or is different
                if (mUser != null)
                {
                    if (mUser.UserName != currentOrder.UserName)
                    {
                        currentOrder.UserName = mUser.UserName;
                        
                        // update shopperID cookie too
                        HttpContext.Current.Response.Cookies["shopperID"].Domain = Utility.GetBaseDomain();
                        HttpContext.Current.Response.Cookies["shopperID"].Value = currentOrder.UserName;
                        HttpContext.Current.Response.Cookies["shopperID"].Expires = DateTime.Today.AddDays(365);
                    }

                }

                // force page validation before we process the order
                Page.Validate();

                if (Page.IsValid)
                {
                    if (Request.QueryString["token"] != null)
                    {
                        // PayPal ExpressCheckout - DoExpressCheckout
                        transactionID = RunExpressCheckout();
                    }
                    else
                    {
                        // process the payment - its a CC direct pay
                        transactionID = RunCharge();
                    }

                    // here's where you might put your integration logic for a fullfillment system
                    if (bool.Parse(ConfigurationManager.AppSettings.Get("useIntegrator")))
                    {
                        // here's where we populate the Integration Address table
                        SaveIntegrationAdress();
                    }

                    //if an error occurred, the transactionId will be null or empty. 
                    if (!String.IsNullOrEmpty(transactionID))
                    {
                        // set or update the orderId cookie
                        HttpContext.Current.Response.Cookies["orderId"].Domain = Utility.GetBaseDomain();
                        HttpContext.Current.Response.Cookies["orderId"].Value = currentOrder.OrderID.ToString();
                        HttpContext.Current.Response.Cookies["orderId"].Expires = DateTime.Today.AddDays(1);

                        // transfer to receipt
                        Response.Redirect("checkout4.aspx", true);
                    }
                    else
                    {
                        message.Text = "There was an issue processing your payment, please try again.";
                    }
                }

            }
            catch (Exception ex)
            {
                message.Text = "We're sorry, an error occured.<br />" + ex.Message;
            }
        }

        /// <summary>
        /// Handles the Click event of the backButton control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        protected void backButton_Click(object sender, EventArgs e)
        {
            // back to address entry
            Response.Redirect("checkout2.aspx", true);
        }

        /// <summary>
        /// Handles the SelectedIndexChanged event of the radShipChoices control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        protected void radShipChoices_SelectedIndexChanged(object sender, EventArgs e)
        {
            //set the Profile Shipping Bits
            SetProfile(currentOrder.Email);

            umbProfile.CurrentOrderShipping = radShipChoices.SelectedValue;
            umbProfile.CurrentOrderShippingMethod = radShipChoices.SelectedItem.Text;

            SetOrderInfo();  

            // update the order display
            lblOrderItems.Text = currentOrder.mcOrderItemsTable();
            orderRunningTotal.Text = currentOrder.OrderTotal.ToString("c");
        }

        #endregion

        #region checkout utility methods

        /// <summary>
        /// Sets the order info for address, shipping, and tax
        /// </summary>
        protected void SetOrderInfo()
        {
            if (radShipChoices != null && radShipChoices.Items.Count > 0)
            {
                if (radShipChoices.SelectedValue == "")
                    currentOrder.ShippingAmount = 0;
                else
                    currentOrder.ShippingAmount = decimal.Parse(radShipChoices.SelectedValue);

                if (radShipChoices.SelectedIndex == -1)
                    umbProfile.CurrentOrderShippingMethod = "none";
                else
                    currentOrder.ShippingMethod = radShipChoices.SelectedItem.Text;
            }

            currentOrder.ShippingAddress = umbProfile.GetCommerceShippingAddress();
            currentOrder.BillingAddress = umbProfile.GetCommerceBillingAddress();

            // save the addresses as HTML strings for ERP integration
            currentOrder.ShipToAddress = currentOrder.ShippingAddress.ToHtmlString();
            currentOrder.BillToAddress = currentOrder.BillingAddress.ToHtmlString();

            // get tax
            currentOrder.TaxAmount = GetCurrentOrderTax(umbProfile.BillingStateOrRegion);

            //pull the CC info from the PaymentBox
            currentOrder.CreditCardNumber = CleanData(paymentBox.CCNumber);
            currentOrder.CreditCardExpireMonth = paymentBox.ExpirationMonth;
            currentOrder.CreditCardExpireYear = paymentBox.ExpirationYear;
            currentOrder.CreditCardSecurityNumber = paymentBox.SecurityCode;
            currentOrder.CreditCardType = paymentBox.CCType;

            if (currentOrder.BillingAddress != null)
            {
                currentOrder.FirstName = currentOrder.BillingAddress.FirstName;
                currentOrder.LastName = currentOrder.BillingAddress.LastName;
                currentOrder.Email = currentOrder.BillingAddress.Email;
            }

        }

        /// <summary>
        /// Sets the current order based on the "shopperID" cookie value
        /// </summary>
        protected void SetCurrentOrder()
        {
            if (currentOrder == null)
                currentOrder = OrderController.GetCurrentOrder();
        }

        /// <summary>
        /// Sets the profile to the current user and initializes ProfileBase
        /// </summary>
        /// <param name="userName">Name of the user.</param>
        protected void SetProfile(string userName)
        {
            // set the Umbraco profile
            umbProfile = new CommerceForUmbraco.profile.UmbracoProfile(userName);
        }

        /// <summary>
        /// Sets the PayPal ExpressCheckout return page level items
        /// (shipping info, profile, and tax)
        /// </summary>
        protected void SetExpressReturn()
        {
            Address ShipTo = umbProfile.GetCommerceShippingAddress();
                //(Address)profile.GetPropertyValue("LastShippingAddress");
            // get the shipping options if we have a shipping address (zip + country)
            if ((ShipTo.Zip.Length > 0) && (ShipTo.Country.Length > 0))
            {
                SetCurrentOrder();
                BindShipping(LoadPackage());

                // update the order display to reflect default shipping and tax
                currentOrder.ShippingAmount = decimal.Parse(radShipChoices.SelectedValue);
                currentOrder.ShippingMethod = radShipChoices.SelectedItem.Text;
                
                // also set tax for the currentOrder
                currentOrder.TaxAmount = GetCurrentOrderTax(umbProfile.BillingStateOrRegion);

                umbProfile.CurrentOrderTax = currentOrder.TaxAmount.ToString();
            }
        }

        /// <summary>
        /// Sets the shipping options based on shipping address
        /// </summary>
        protected void SetShippingOptions()
        {
            PackageInfo package = LoadPackage();

            if (currentOrder.ShippingAddress != null && currentOrder.BillingAddress != null)
            {
                BindShipping(package);

                // update the order to reflect default shipping and tax
                if (radShipChoices.SelectedValue == "")
                    currentOrder.ShippingAmount = 0;
                else
                    currentOrder.ShippingAmount = decimal.Parse(radShipChoices.SelectedValue);

                if (radShipChoices.SelectedIndex == -1)
                    umbProfile.CurrentOrderShippingMethod = "none";
                else
                    currentOrder.ShippingMethod = radShipChoices.SelectedItem.Text;

                // also set tax for the currentOrder
                currentOrder.TaxAmount = GetCurrentOrderTax(umbProfile.BillingStateOrRegion);

                umbProfile.CurrentOrderTax = currentOrder.TaxAmount.ToString();
            }
        }

        /// <summary>
        /// Saves the addresses to the user Profile
        /// </summary>
        protected void SaveAddresses()
        {
            // saves the entered address to the user profile
            SetProfile(currentOrder.Email);

            // Shipping Address
            umbProfile.ShippingFirstName = currentOrder.ShippingAddress.FirstName;
            umbProfile.ShippingLastName = currentOrder.ShippingAddress.LastName;
            umbProfile.ShippingEmail = currentOrder.ShippingAddress.Email;
            umbProfile.ShippingAddress1 = currentOrder.ShippingAddress.Address1;
            umbProfile.ShippingAddress2 = currentOrder.ShippingAddress.Address2;
            umbProfile.ShippingCity = currentOrder.ShippingAddress.City;
            umbProfile.ShippingCountry = currentOrder.ShippingAddress.Country;
            umbProfile.ShippingStateOrRegion = currentOrder.ShippingAddress.StateOrRegion;
            umbProfile.ShippingZip = currentOrder.ShippingAddress.Zip;
            umbProfile.ShippingPhone = currentOrder.ShippingAddress.Phone;
            
            //Billing Address
            umbProfile.BillingFirstName = currentOrder.BillingAddress.FirstName;
            umbProfile.BillingLastName = currentOrder.BillingAddress.LastName;
            umbProfile.BillingEmail = currentOrder.BillingAddress.Email;
            umbProfile.BillingAddress1 = currentOrder.BillingAddress.Address1;
            umbProfile.BillingAddress2 = currentOrder.BillingAddress.Address2;
            umbProfile.BillingCity = currentOrder.BillingAddress.City;
            umbProfile.BillingCountry = currentOrder.BillingAddress.Country;
            umbProfile.BillingStateOrRegion = currentOrder.BillingAddress.StateOrRegion;
            umbProfile.BillingZip = currentOrder.BillingAddress.Zip;
            umbProfile.BillingPhone = currentOrder.BillingAddress.Phone;

            // Commerce Specifics
            umbProfile.PayPalPayerID = Request.QueryString.Get("PayerID");
            umbProfile.PayPalToken = Request.QueryString.Get("token");
            umbProfile.CurrentOrderTax = currentOrder.TaxAmount.ToString();

        }

        /// <summary>
        /// Saves the order address to the integration adress table
        /// </summary>
        protected void SaveIntegrationAdress()
        {
            IntegrationAddress shipping = null;
            IntegrationAddress billing = null;

            // check to see if we have records for this order already
            // SHIPPING
            int shipExistsCount = new SubSonic.Query(IntegrationAddress.Schema)
                .AddWhere(IntegrationAddress.Columns.AddressType, "SHIP")
                .AddWhere(IntegrationAddress.Columns.OrderId, currentOrder.OrderID.ToString()).GetCount("orderId");

            if (shipExistsCount > 0)
            {
                // update shipAddress
                string addressId = new SubSonic.Select(IntegrationAddress.Columns.AddressId)
                    .From(IntegrationAddress.Schema)
                    .WhereExpression(IntegrationAddress.Columns.AddressType).IsEqualTo("SHIP")
                    .And(IntegrationAddress.Columns.OrderId).IsEqualTo(currentOrder.OrderID.ToString())
                    .ExecuteScalar<string>();

                shipping = new IntegrationAddress(addressId);
            }
            else
            {
                shipping = new IntegrationAddress();
            }
            // BILLING
            int billExistsCount = new SubSonic.Query(IntegrationAddress.Schema)
                .AddWhere(IntegrationAddress.Columns.AddressType, "BILL")
                .AddWhere(IntegrationAddress.Columns.OrderId, currentOrder.OrderID.ToString()).GetCount("orderId");

            if (billExistsCount > 0)
            {
                // update shipAddress
                string addressId = new SubSonic.Select(IntegrationAddress.Columns.AddressId)
                    .From(IntegrationAddress.Schema)
                    .WhereExpression(IntegrationAddress.Columns.AddressType).IsEqualTo("BILL")
                    .And(IntegrationAddress.Columns.OrderId).IsEqualTo(currentOrder.OrderID.ToString())
                    .ExecuteScalar<string>();

                billing = new IntegrationAddress(addressId);
            }
            else
            {
                billing = new IntegrationAddress();
            }

            try
            {
                shipping.AddressType = "SHIP";
                shipping.OrderId = currentOrder.OrderID;
                shipping.FirstName = currentOrder.ShippingAddress.FirstName;
                shipping.LastName = currentOrder.ShippingAddress.LastName;
                shipping.Email = currentOrder.ShippingAddress.Email;
                shipping.Address1 = TrimData(currentOrder.ShippingAddress.Address1, 40);
                shipping.Address2 = TrimData(currentOrder.ShippingAddress.Address2, 40);
                shipping.City = TrimData(currentOrder.ShippingAddress.City, 25);
                shipping.Country = TrimData(currentOrder.ShippingAddress.Country, 3);
                shipping.State = TrimData(currentOrder.ShippingAddress.StateOrRegion, 3);
                shipping.Zip = TrimData(currentOrder.ShippingAddress.Zip, 12);
                shipping.Phone = "0";

                shipping.Save("checkout");

                billing.AddressType = "BILL";
                billing.OrderId = currentOrder.OrderID;
                billing.FirstName = currentOrder.BillingAddress.FirstName;
                billing.LastName = currentOrder.BillingAddress.LastName;
                billing.Email = currentOrder.BillingAddress.Email;
                billing.Address1 = TrimData(currentOrder.BillingAddress.Address1, 40);
                billing.Address2 = TrimData(currentOrder.BillingAddress.Address2, 40);
                billing.City = TrimData(currentOrder.BillingAddress.City, 25);
                billing.Country = TrimData(currentOrder.BillingAddress.Country, 3);
                billing.State = TrimData(currentOrder.BillingAddress.StateOrRegion, 3);
                billing.Zip = TrimData(currentOrder.BillingAddress.Zip, 12);
                billing.Phone = "0";

                billing.Save("checkout");

            }
            catch (Exception ex)
            {
                // swallow the error for now - we've already processed the order
                message.Text = "Your order was successfully placed but we're sorry, an error occured.<br />" + ex.Message;
            }

        }

        /// <summary>
        /// Runs the charge for CC payments
        /// </summary>
        /// <returns></returns>
        protected string RunCharge()
        {
            string sOut = "";

            try
            {
                Transaction trans = OrderController.TransactOrder(currentOrder, TransactionType.CreditCardPayment);
                //the orderID is set during the TransactOrder process
                sOut = currentOrder.OrderGUID;
            }
            catch (Exception x)
            {
                throw x;
            }

            return sOut;
        }

        /// <summary>
        /// Loads the package information need for page display and 
        /// shipping provider calculations
        /// </summary>
        /// <returns></returns>
        protected PackageInfo LoadPackage()
        {
            //Create shipping package
            SiteConfig.Load();

            PackageInfo package = new PackageInfo();
            package.FromZip = SiteConfig.ShipFromZip;
            package.FromCountryCode = SiteConfig.ShipFromCountryCode;

            if ((Address)umbProfile.GetCommerceShippingAddress() != null)
            {
                Address shipAdd = (Address)umbProfile.GetCommerceShippingAddress();
                package.ToZip = shipAdd.Zip;

                // this is a workaround for the country mis-match between PayPal and UPS
                //TODO: parse on a per provider basis based on expected format
                switch (shipAdd.Country)
                {
                    case "United States":
                        package.ToCountryCode = "US";
                        break;
                    case "Canada":
                        package.ToCountryCode = "CA";
                        break;
                    case "US":
                        package.ToCountryCode = "US";
                        break;
                    case "CA":
                        package.ToCountryCode = "CA";
                        break;
                    default:
                        package.ToCountryCode = "US";
                        break;
                }
            }

            package.Weight = currentOrder.GetItemsWeight();
            package.Width = Convert.ToInt16(currentOrder.GetTotalWidth());
            package.Height = Convert.ToInt16(currentOrder.GetTotalHeight());
            package.Length = Convert.ToInt16(currentOrder.GetMaxLength());
            package.DimensionUnit = SiteConfig.DimensionUnit;
            package.PackagingBuffer = SiteConfig.ShipPackagingBuffer;

            //Create Dictionary args for future expansion options
            //package.Args = new Dictionary<string, string>();
            return package;

        }

        /// <summary>
        /// Binds the shipping option to the order
        /// and updates the option list based on custom
        /// config settings
        /// </summary>
        /// <param name="package">The package.</param>
        protected void BindShipping(PackageInfo package)
        {
            // send the order amount and number of units
            int numberUnits = 0;
            foreach (OrderItem i in currentOrder.Items)
            {
                numberUnits += i.Quantity;
            }

            Commerce.Providers.DeliveryOptionCollection options =
                Commerce.Providers.FulfillmentService.GetOptions
                (package, (currentOrder.OrderTotal - currentOrder.ShippingAmount), numberUnits);

            radShipChoices.DataSource = options;
            radShipChoices.DataTextField = "Service";
            radShipChoices.DataValueField = "Rate";
            radShipChoices.DataBind();

            foreach (DeliveryOption d in options)
            {
                if (d.Service.Contains("Free Shipping"))
                {
                    // select Free Shipping is it's an option
                    radShipChoices.SelectedValue = d.Rate.ToString();
                    break;
                }
                else if (d.Service.Contains("Flat Rate"))
                {
                    // select Flat Rate Shipping is it's an option
                    radShipChoices.SelectedValue = d.Rate.ToString();
                    break;
                }
            }

            //localize it using the C formatter for local currency
            decimal dRate = 0;
            foreach (ListItem l in radShipChoices.Items)
            {
                dRate = decimal.Parse(l.Value);
                l.Text += ": " + dRate.ToString("C");
            }

            //set the Profile Shipping Bits
            umbProfile.CurrentOrderShipping = radShipChoices.SelectedValue;
            if (radShipChoices.SelectedIndex == -1)
                umbProfile.CurrentOrderShippingMethod = "none";
            else
                umbProfile.CurrentOrderShippingMethod = radShipChoices.SelectedItem.Text;
        }

        /// <summary>
        /// Gets the current order tax based on supplied State Code
        /// </summary>
        /// <returns>Tax Amount</returns>
        protected decimal GetCurrentOrderTax(string StateCode)
        {
            decimal dTax = 0;

            try
            {
                USState state = (USState)Utility.StringToEnum(typeof(Commerce.Common.USState), StateCode);

                //dTax = OrderController.CalculateTax(state, currentOrder.CalculateSubTotal());
                dTax = OrderController.CalculateTax(state, currentOrder.CalculateSubTotal(), currentOrder.ShippingAmount);

                return dTax;
            }
            catch
            {
                return 0;
            }
        }

        /// <summary>
        /// Trims string input to the specified length
        /// </summary>
        /// <param name="data">The data.</param>
        /// <param name="length">The length.</param>
        /// <returns></returns>
        protected string TrimData(string data, int length)
        {
            int totalLength = data.Length;

            if (totalLength > length)
            {
                return data.Substring(0, length);
            }
            else
            {
                return data;
            }
        }

        /// <summary>
        /// Cleans the data of spaces and non-numerics
        /// </summary>
        /// <param name="data">string data.</param>
        /// <returns></returns>
        protected string CleanData(string data)
        {
            string output = "";
            System.Text.RegularExpressions.Regex reg = new System.Text.RegularExpressions.Regex(@"[\s+]?[\.+]?[\-+]?");
            output = reg.Replace(data, "");
            
            return output;
        }

    #endregion

        #region PayPal Express Checkout

        /// <summary>
        /// Gets the PayPal wrapper need to make API calls
        /// </summary>
        /// <returns></returns>
        protected APIWrapper GetPPWrapper()
        {
            SiteConfig.Load();

            APIWrapper wrapper =
              new APIWrapper(SiteConfig.PayPalAPIAccountName
                    , SiteConfig.PayPalAPIAccountPassword
                    , SiteConfig.APIsignature
                    , (CurrencyCode)Enum.Parse(typeof(CurrencyCode),SiteConfig.defaultCurrencyCode)
                    , SiteConfig.APIIsLive);

            return wrapper;
        }

        /// <summary>
        /// Processes the PayPal ExpressCheckout return
        /// sets up page level items and save address 
        /// changes to order and profile
        /// </summary>
        /// <param name="sToken">The s token.</param>
        protected void ProcessExpressReturn(string sToken)
        {
            try
            {
                //get the wrapper
                APIWrapper wrapper = GetPPWrapper();

                // back from the PayPal site
                Commerce.Common.Address payer = wrapper.GetExpressCheckout(sToken);

                // set the order
                SetCurrentOrder();

                // set the email and addresses
                currentOrder.Email = payer.Email;
                currentOrder.BillingAddress = payer;
                currentOrder.ShippingAddress = payer;
                //SaveAddresses();
                OrderController.SaveAddress(payer);

                //set the token in the ViewState 
                ViewState.Add("ppToken", payer.PayPalToken);
                ViewState.Add("ppID", payer.PayPalPayerID);

                // PVS - 5/23/2008- change to use tax calc by state rather than ZIP
                USState state = (USState)Utility.StringToEnum(typeof(Commerce.Common.USState), payer.StateOrRegion);

                currentOrder.TaxAmount = OrderController.CalculateTax(state, currentOrder.CalculateSubTotal());

                //save down the order to the ViewState so that it can be picked up when Running the Charge
                SetOrderInfo();

            }
            catch (Exception)
            {
                throw;
            }
        }

        /// <summary>
        /// Runs the PayPal ExpressCheckout to capture funds
        /// </summary>
        /// <returns></returns>
        protected string RunExpressCheckout()
        {
            //this is the PayPal response holder
            string orderID = "";

            try
            {
            string sToken = Utility.GetParameter("token");
            
            //SaveAddresses();
            SetOrderInfo();

            //run the Final Express Checkout
                Transaction trans = OrderController.TransactOrder(currentOrder, TransactionType.PayPalPayment);
                orderID = currentOrder.OrderGUID;

                return orderID;
            }
            catch (Exception)
            {
                throw;
            }

        }

        #endregion

    }
}